package tony.test.utils

import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import kotlin.collections.iterator

/**
 * 注解代理工具类
 *
 * 用于创建注解实例的动态代理，解决 Mockito 无法正确处理 Kotlin KClass 类型的问题。
 * 通过 Java 反射的动态代理机制，可以创建任意注解的实例并设置其属性值。
 *
 * @author tangli
 * @date 2025/01/23 16:00
 */
object AnnotationProxy {

    /**
     * 创建注解实例的动态代理
     *
     * @param T 注解类型
     * @param annotationClass 注解类
     * @param properties 注解属性映射，键为属性名，值为属性值
     * @return 注解实例代理
     */
    @Suppress("UNCHECKED_CAST")
    fun <T : Annotation> create(
        annotationClass: Class<T>,
        properties: Map<String, Any>
    ): T {
        val handler = AnnotationInvocationHandler(annotationClass, properties)
        return Proxy.newProxyInstance(
            annotationClass.classLoader,
            arrayOf(annotationClass),
            handler
        ) as T
    }

    /**
     * 注解调用处理器
     *
     * 实现 InvocationHandler 接口，处理注解方法的调用。
     * 对于注解属性方法，返回预设的属性值；
     * 对于 annotationType() 方法，返回注解类型；
     * 对于 toString()、hashCode()、equals() 等方法，提供默认实现。
     *
     * @param annotationClass 注解类
     * @param properties 注解属性映射
     */
    private class AnnotationInvocationHandler(
        private val annotationClass: Class<out Annotation>,
        private val properties: Map<String, Any>
    ) : InvocationHandler {

        override fun invoke(proxy: Any?, method: Method, args: Array<out Any>?): Any? {
            return when (method.name) {
                "annotationType" -> annotationClass
                "toString" -> buildToString()
                "hashCode" -> buildHashCode()
                "equals" -> buildEquals(proxy, args?.get(0))
                else -> {
                    // 处理注解属性方法
                    val propertyName = method.name
                    properties[propertyName] ?: getDefaultValue(method)
                }
            }
        }

        /**
         * 获取方法的默认值
         *
         * @param method 方法
         * @return 默认值
         */
        private fun getDefaultValue(method: Method): Any? {
            return method.defaultValue
        }

        /**
         * 构建 toString 方法的返回值
         *
         * @return toString 字符串
         */
        private fun buildToString(): String {
            val className = annotationClass.simpleName
            val propertiesStr = properties.entries.joinToString(", ") { (key, value) ->
                "$key=$value"
            }
            return "@$className($propertiesStr)"
        }

        /**
         * 构建 hashCode 方法的返回值
         *
         * @return hashCode 值
         */
        private fun buildHashCode(): Int {
            var result = annotationClass.hashCode()
            for ((key, value) in properties) {
                result = 31 * result + key.hashCode()
                result = 31 * result + value.hashCode()
            }
            return result
        }

        /**
         * 构建 equals 方法的返回值
         *
         * @param proxy 代理对象
         * @param other 比较对象
         * @return 是否相等
         */
        private fun buildEquals(proxy: Any?, other: Any?): Boolean {
            if (proxy === other) return true
            if (other == null) return false
            if (!annotationClass.isInstance(other)) return false

            // 比较所有属性值
            for ((key, value) in properties) {
                try {
                    val otherMethod = annotationClass.getMethod(key)
                    val otherValue = otherMethod.invoke(other)
                    if (value != otherValue) return false
                } catch (_: Exception) {
                    return false
                }
            }
            return true
        }
    }
}
